package com.jiepos.mpos.inter.intopieces.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.jiepos.mpos.core.util.StringUtils;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * @ClassName: SignatureUtil
 * @Description: 签名加解密工具类
 * @author 孙永
 * @date 2018年2月26日 上午9:32:19
 */
public class SignatureUtil {
	
	private static final int MAX_DECRYPT_BLOCK = 128;
	private static final int MAX_ENCRYPT_BLOCK = 117;
	private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
	private static final String DESEDE = "DESede";
	private static final String KEY_ALGORITHM = "RSA";
	private static final String CHARSET_UTF8 = "UTF-8";
	private static final String PRIVATE_KEY = "privateKey";
	private static final String PUBLIC_KEY = "publicKey";
	
	/**
     * 生成密钥对(公钥和私钥)
     * @return
     * @throws Exception
     */
    public static Map<String, String> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, String> map = new HashMap<>();
        map.put(PUBLIC_KEY, encryptBASE64(publicKey.getEncoded()));
        map.put(PRIVATE_KEY, encryptBASE64(privateKey.getEncoded()));
        return map;
    }
    
    /**
     * 用私钥加密
     * @param data 需要加密的数据 
     * @param publicKey 私钥
     * @return 密文
     * @throws Exception
     */
    public static String encryptByPrivateKey(Map<String, String> data, String privateKey){
    	if(StringUtils.isBlank(privateKey)){
    		return null;
    	}
    	return encryptByPrivateKey(mapToString(data), privateKey);
    }
  
    /** 
     * 用私钥加密 
     * @param ciphertext 需要加密的数据 
     * @param privateKey 私钥
     * @return 密文
     * @throws Exception 
     */  
    public static String encryptByPrivateKey(String data, String privateKey){
    	try{
    		// 对密钥解密 
            byte[] keyBytes = decryptBASE64(privateKey);
            // 取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key key = keyFactory.generatePrivate(pkcs8KeySpec);
            // 对数据加密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, key);
            
            byte[] datas = data.getBytes();
            int inputLen = datas.length;
    		ByteArrayOutputStream out = new ByteArrayOutputStream();
    		int offSet = 0;
    		byte[] cache;
    		int i = 0;
    		// 对数据分段加密
    		while (inputLen - offSet > 0) {
    			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
    				cache = cipher.doFinal(datas, offSet, MAX_ENCRYPT_BLOCK);
    			} else {
    				cache = cipher.doFinal(datas, offSet, inputLen - offSet);
    			}
    			out.write(cache, 0, cache.length);
    			i++;
    			offSet = i * MAX_ENCRYPT_BLOCK;
    		}
    		byte[] encryptedData = out.toByteArray();
    		out.close();
            return encryptBASE64(encryptedData);
    	}catch(Exception e){
    		e.printStackTrace();
    		return null;
    	}
    }
	
	/** 
     * 用公钥解密 
     * @param ciphertext 需要解密的密文
     * @param publicKey 公钥
     * @return 原始Map集合数据
     * @throws Exception 
     */ 
    public static Map<String, String> decryptToMapByPublicKey(String ciphertext, String publicKey){
    	return stringToMap(decryptByPublicKey(ciphertext, publicKey));
    }
    
    /** 
     * 用公钥解密 
     * @param ciphertext 需要解密的密文
     * @param publicKey 公钥
     * @return 原始字符串数据
     * @throws Exception 
     */  
    public static String decryptByPublicKey(String ciphertext, String publicKey){
    	try{
    		byte[] datas = decryptBASE64(ciphertext);
            // 对密钥解密
            byte[] keyBytes = decryptBASE64(publicKey);
            // 取得公钥
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key key = keyFactory.generatePublic(x509KeySpec);
            // 对数据解密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, key);
            int inputLen = datas.length;
    	    ByteArrayOutputStream out = new ByteArrayOutputStream();
    	    int offSet = 0;
    	    byte[] cache;
    	    int i = 0;
    	    // 对数据分段解密
    	    while (inputLen - offSet > 0) {
    	        if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
    	            cache = cipher.doFinal(datas, offSet, MAX_DECRYPT_BLOCK);
    	        } else {
    	            cache = cipher.doFinal(datas, offSet, inputLen - offSet);
    	        }
    	        out.write(cache, 0, cache.length);
    	        i++;
    	        offSet = i * MAX_DECRYPT_BLOCK;
    	    }
    	    byte[] decryptedData = out.toByteArray();
    	    out.close();
            return new String(decryptedData);
    	}catch(Exception e){
    		e.printStackTrace();
    		return null;
    	}
    }
    
    /**
     * 3DES加密
     * @param key
     * @param data 需要加密的数据
     * @return
     */
    public static String encrypt3DES(String key, String data){
    	try{
    		byte keys[] = get3DesKey(key);
    		byte datas[] = data.getBytes();
            SecretKey ky = new SecretKeySpec(keys, DESEDE);
            Cipher c = Cipher.getInstance(DESEDE);
            c.init(Cipher.ENCRYPT_MODE, ky);
            return byteArrayToHexString(c.doFinal(datas));
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 3DES解密
     * @param key
     * @param data
     * @return
     */
    public static String decrypt3DES(String key, String data) {
    	try {
    		byte keys[] = get3DesKey(key);
    		byte datas[] = hexToBytes(data);
            SecretKey ky = new SecretKeySpec(keys, DESEDE);
            Cipher c = Cipher.getInstance(DESEDE);
            c.init(Cipher.DECRYPT_MODE, ky);
            return new String(c.doFinal(datas));
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * MD5加密
     * @param data map集合
     * @return
     */
    public static String MD5Encode(Map<String, String> data){
    	return MD5Encode(getASCIISort(data));
    }
    
    /**
     * MD5加密
     * @param data 需要加密的字符串
     * @return
     */
    private static String MD5Encode(String data){
    	try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(data.getBytes(CHARSET_UTF8));
            return byteArrayToHexString(md.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 字符串转字节数组
     * @param str
     * @return
     */
    private static byte[] hexToBytes(String str){
        if(str == null){
            return null;
        }else if(str.length() < 2){
            return null;
        }else{
            int len = (str.length() / 2 );
            byte[] buffer = new byte[len];
            for(int i = 0; i < len; i++){
                buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
            }
            return buffer;
        }
    }
    
    /**
     * 转换字节数组为16进制字串
     * @param b 字节数组
     * @return 16进制字串
     */
    private static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for(byte aB : b){
        	int n = aB;
            if (n < 0) {
                n = 256 + n;
            }
            int d1 = n / 16;
            int d2 = n % 16;
            resultSb.append(hexDigits[d1] + hexDigits[d2]);
        }
        return resultSb.toString();
    }
    
    /**
     * map集合转字符串通过&符号连接
     * @param data
     * @return
     */
    private static String mapToString(Map<String, String> data){
    	if(data == null || data.isEmpty()){
    		return null;
    	}
    	StringBuffer originalData = new StringBuffer();
    	for(Map.Entry<String,String> entry : data.entrySet()){
    		originalData.append("&").append(entry.getKey()).append("=").append(StringUtils.isBlank(entry.getValue()) ? "" : entry.getValue());
        }
    	return originalData.toString().replaceFirst("&", "");
    }
    
    /**
     * map集合转字符串通过&符号连接
     * @param data
     * @return
     */
    private static Map<String, String> stringToMap(String data){
    	if(StringUtils.isBlank(data)){
    		return null;
    	}
    	Map<String, String> map = new HashMap<>();
    	String[] datas = data.split("&");
    	for(String param : datas){
    		map.put(param.substring(0, param.indexOf("=")), param.substring(param.indexOf("=") + 1));
    	}
    	return map;
    }
    
    private static byte[] decryptBASE64(String key){
        byte[] output = null;
        try {
            output = (new BASE64Decoder()).decodeBuffer(key);
            return output;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return output;
    }
    
    private static String encryptBASE64(byte[] keyBytes){
        return (new BASE64Encoder()).encode(keyBytes);
    }
    
    /**
     * 集合按照ASCII排序生成字符串
     * @param params
     * @return
     */
    private static String getASCIISort(Map<String, String> params){
    	List<String> list = new ArrayList<>();
        for(Map.Entry<String,String> entry : params.entrySet()){
            if(StringUtils.isNotBlank(entry.getValue())){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String data = sb.toString();
        if(StringUtils.isNotBlank(data)){
            data = data.substring(0, data.length()-1);
        }
        return data;
    }
    
    /**
     * 生成24字节的3DES密钥,不够24字节，则补0；超过24字节，则取前24字节
     * @param key 密钥字符串
     * @return
     */
    public static byte[] get3DesKey(String key){
        byte[] keyBytes = new byte[24];
        if(key.getBytes().length > 24){
            for(int i = 0;i<24;i++){
                keyBytes[i] = key.getBytes()[i];
            }
        }else{
            for(int i = 0;i<24;i++){
                if(i < key.getBytes().length){
                    keyBytes[i] = key.getBytes()[i];
                }else{
                    keyBytes[i] = 0x00;
                }
            }
        }
        return keyBytes;
    }
    
    public static void main(String[] args) {
    	System.out.println(encrypt3DES("1E4A1B03D1B6CD8A174A826F76E009F4", "7288932D75224921BEF3B57D049C1931"));
	}
    
}
